﻿using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation;

namespace NewRetail.Application.Core
{
    public class MvcExceptionFilter : IAsyncExceptionFilter, ITransientDependency
    {
        private readonly ILogger logger;
        private readonly IWebHostEnvironment environment;

        public MvcExceptionFilter(ILogger<MvcExceptionFilter> logger, IWebHostEnvironment environment)
        {
            this.logger = logger;
            this.environment = environment;
        }

        async Task<ErrorInfo> ConvertToErrorInfo(ExceptionContext context, Exception e)
        {
            if (e is UserFriendlyException err)
            {
                return new ErrorInfo() { Message = err.Message };
            }

            await Task.CompletedTask;
            return null;
        }

        void AddErrorLogging(Exception e, HttpContext context)
        {
            var sb = new StringBuilder();

            sb.AppendLine(context.Request.Path);

            sb.AppendLine(e.Message);

            if (e is AbpValidationException validationException)
            {
                var errors = validationException.ValidationErrors.Select(x => $"{x.MemberNames}->{x.ErrorMessage}").ToArray();
                foreach (var m in errors)
                {
                    sb.AppendLine(m);
                }
            }

            var msg = sb.ToString();
            this.logger.LogError(e, msg);
        }

        public async Task OnExceptionAsync(ExceptionContext context)
        {
            var e = context.Exception;

            if (e == null || context.ExceptionHandled)
            {
                return;
            }

            if (context.HttpContext.Response.HasStarted)
            {
                //this.logger.LogError(exception: e, message: e.Message);
                return;
            }

            context.HttpContext.Response.Clear();

            var error = await this.ConvertToErrorInfo(context, e);
            if (error == null)
            {
                //不能处理的异常统一捕捉并写日志
                error = new ErrorInfo()
                {
                    FriendlyError = false,
                    HttpStatusCode = HttpStatusCode.InternalServerError
                };
                if (this.environment.IsDevelopment())
                {
                    var errBuilder = new StringBuilder();
                    errBuilder.AppendLine($"-------{e.Message}--------");
                    errBuilder.AppendLine(e.StackTrace);
                    error.Message = errBuilder.ToString();
                }
                else
                {
                    error.Message = "服务器错误";
                }
                this.AddErrorLogging(e, context.HttpContext);
            }

            var response = new ResponseEntity(error);
            //text/json指定了input type，那么不是input type的字段是不会序列化的。
            //（派生类字段）
            var result = new JsonResult(response);
            if (error.HttpStatusCode != null)
            {
                result.StatusCode = (int)error.HttpStatusCode.Value;
            }

            context.Result = result;
            context.Exception = null;
            context.ExceptionHandled = true;

            await Task.CompletedTask;
        }
    }
}
